package com.yunkeji.common.utils;

import cn.hutool.core.lang.UUID;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据上报工具类
 *
 * @author 雷擎宇
 */
@Slf4j
public class DataUploadUtils {
    /**
     * 事件类型整合
     */
    @Getter
    @AllArgsConstructor
    public enum DataType {
        注册(VIVOCvType.自定义注册.getCode(), OPPODataType.注册.getCode(), XiaoMiDataType.自定义注册.getCode()),
        支付(VIVOCvType.自定义付费.getCode(), OPPODataType.应用付费.getCode(), XiaoMiDataType.付费.getCode());

        // 对接新渠道后，新增字段即可
        private final String vivo;
        private final int oppo;
        private final String xiaomi;
    }

    // 包名
    private static final String PKG = "com.shouhulife.chujian";

    /**
     * ------------------------------------------------------------VIVO---------------------------------------------------
     */

    /**
     * 数据源类型
     */
    private enum VIVOSrcType {
        APP,
        WEB,
        OFFLINE
    }

    /**
     * 用户标识类型
     */
    private enum VIVOUserIdType {
        IMEI,
        IMEI_MD5,
        OAID,
        OAID_MD5,
        OTHER
    }

    /**
     * 事件类型
     */
    @Getter
    @AllArgsConstructor
    private enum VIVOCvType {
        自定义激活("ACTIVATION"),
        自定义注册("REGISTER"),
        自定义付费("PAY"),
        每一次付费("PAY_ONETIME"),
        表单提交("SUBMIT"),
        网页购买("WEBPAY"),
        自定义次留("RETENTION_1"),
        拨打电话("CALL"),
        自定义拉活("REACTIVATION"),
        添加到桌面("ADD_SCREEN"),
        按钮点击("BUTTON_CLICK"),
        自定义授信("CREDIT"),
        其他_个性化事件使用("OTHER"),
        拉活_进入详情页("REACTIVATION_DPCLICK"),
        拉活自定义次留("REACTIVATION_RETENTION"),
        微信_识别二维码("IDENTIFY_CODE"),
        微信_添加微信("ADD_WECHAT"),
        微信_关注公众号("ADD_MPA"),
        微信_用户首次消息("DIALOGUE_MPA"),
        有效咨询("ONE_DIALOGUE"),
        咨询线索("LEAVE_CLUE"),
        微信小游戏注册("WECHATGAME_REGISTER"),
        微信小游戏付费("WECHATGAME_PAY");

        private final String code;
    }

    /**
     * VIVO配置类
     */
    private static class VIVOConfig {
        private static final String URL = "https://ocpx.mktad.cn/openapi/v1/advertiser/behavior/upload";
        // 数据源id
        private static final String SRC_ID = "ds-202108181298";
        // 数据源类型
        private static final String SRC_TYPE = VIVOSrcType.APP.name();
        // 推广账户对应的广告主ID
        private static final String ADVERTISER_ID = "f8dc4dae3f19055cb878";
        // 访问令牌
        private static final String ACCESS_TOKEN = "c9823969aaad97b0fc4003451d2201e62a232d25984bca6efde0fc517394d384";
    }

//    public static void main(String[] args) {
//        vivoUpload("123456test", DataType.注册.getVivo());
//    }

    /**
     * VIVO数据上报
     *
     * @param oaid
     * @param dataType
     */
    public static void vivoUpload(String oaid, DataType dataType) {
        long timestamp = System.currentTimeMillis();
        List<Map<String, Object>> list = new ArrayList<>();
        Map<String, Object> data = new HashMap<>();
        //事件发生的系统时间戳，精确到毫秒，13位
        data.put("cvTime", timestamp);
        //事件类型
        data.put("cvType", dataType.getVivo());
        //获取的设备级ID，OAID或IMEI号等等
        data.put("userId", oaid);
        //用户标识类型
        data.put("userIdType", VIVOUserIdType.OAID.name());
        list.add(data);
        Map<String, Object> datas = new HashMap<>();
        //数据源id
        datas.put("srcId", VIVOConfig.SRC_ID);
        //数据源类型
        datas.put("srcType", VIVOConfig.SRC_TYPE);
        //包名
        datas.put("pkgName", PKG);
        //数据列表
        datas.put("dataList", list);
        String jsonStr = JSONUtil.toJsonStr(datas);

        String nonce = UUID.fastUUID().toString(true);

        MediaType mediaType = MediaType.parse("application/json");

        RequestBody body = RequestBody.create(mediaType, jsonStr);
        Request request = new Request.Builder()
            .url(VIVOConfig.URL + "?access_token=" + VIVOConfig.ACCESS_TOKEN + "&timestamp=" + timestamp + "&nonce=" + nonce + "&advertiser_id=" + VIVOConfig.ADVERTISER_ID)
            .method("POST", body)
            .addHeader("Content-Type", "application/json")
            .build();
        Response response = null;
        try {
            OkHttpClient client = new OkHttpClient().newBuilder().build();
            response = client.newCall(request).execute();
            log.info("=======================>result:{}<===================================", response.body().string());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * ---------------------------------------------------OPPO------------------------------------------------------------
     */

    /**
     * OPPO事件类型
     */
    @Getter
    @AllArgsConstructor
    public enum OPPODataType {
        激活(1),
        注册(2),
        游戏付费(3),
        次留(4),
        应用内授信(5),
        应用内下单_电商(6),
        应用付费(7),
        自定义目标(8),
        第三日留存(9),
        第四日留存(10),
        第五日留存(11),
        第六日留存(12),
        第七日留存(13),
        第八日留存(14),
        拉活(15),
        快应用或小程序付费(16),
        次留_DP(17),
        卸载激活(18),
        快应用关键行为(19),
        游戏或应用关键行为(20),
        长留高质量人群_废弃(21),
        应用付费次数(22),
        长留自定义(23),
        快应用付费次数(24),
        付费流水(25),
        质量分(26),
        完件(27);

        private final int code;
    }

    /**
     * OPPO监测回调接口的请求体
     */
    @Data
    public class OPPORequestBody {
        /**
         * 用户ID
         */
        private String oaid;

        /**
         * 广告id
         */
        private Long adid;

        /**
         * 设备IMEI
         */
        private String imei;

        /**
         * 安卓id
         */
        private String androidid;

        /**
         * 用户ip
         */
        private String ip;

        /**
         * 用户请求标识：user_agent，包含浏览器信息等
         */
        private String ua;

        /**
         * 广告主id
         */
        private String ownerid;

        /**
         * 广告计划id
         */
        private String planid;

        /**
         * 广告组id
         */
        private String groupid;
    }

    /**
     * OPPO配置
     */
    private static class OPPOConfig {
        private static String URL = "https://api.ads.heytapmobi.com/api/uploadActiveData";
        // 1：OPPO，2:一加，0：其他，默认OPPO
        private static int CHANNEL = 1;
        // 0：没有MD5加密，1：IMEI的MD5加密，2：OAID的MD5加密。这里使用的是OAID，传2
        private static int TYPE = 0;
        // 归因类型，1：广告主归因，0：OPPO归因，默认传0
        private static int ASCRIBE_TYPE = 1;
        private static String SALT = "e0u6fnlag06lc3pl";
        private static String BASE_64_KEY = "XGAXicVG5GMBsx5bueOe4w==";
    }

    /**
     * OPPO的数据上传
     *
     * @param ouId     oaid
     * @param dataType
     */
    public static void oppoUpload(String ouId, DataType dataType, long adId) {
        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, Object> datas = new HashMap<>();
        try {
            // OAID，OPPO那边叫OUID
            datas.put("ouId", oppoAES(ouId));
            datas.put("pkg", PKG);
            // 时间戳
            datas.put("timestamp", timestamp);
            // 事件类型
            datas.put("dataType", dataType.getOppo());
            datas.put("channel", OPPOConfig.CHANNEL);
            datas.put("type", OPPOConfig.TYPE);
            datas.put("ascribeType", OPPOConfig.ASCRIBE_TYPE);
            // 广告主回传转化数据时，附带已经归因好的广告ID，详见sys_upload表
            datas.put("adId", adId);
            OkHttpClient client = new OkHttpClient().newBuilder().build();
            MediaType mediaType = MediaType.parse("application/json");
            String jsonStr = JSONUtil.toJsonStr(datas); //使用请求参数的json串替换，格式如下方示例
            RequestBody body = RequestBody.create(mediaType, jsonStr);
            String sign = oppoSignature(jsonStr, timestamp);
            Request request = new Request.Builder()
                .url(OPPOConfig.URL)
                .method("POST", body)
                .addHeader("Content-Type", "application/json")
                .addHeader("timestamp", timestamp)
                .addHeader("signature", sign)
                .build();
            log.info("===========>oppo_data:{}<===========", jsonStr);
            log.info("===========>oppo_sign:{}<===========", sign);
            Response response = client.newCall(request).execute();
            log.info("===========>oppo_result:{}<===========", response.body().string());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * OPPO的签名加密
     *
     * @param postData
     * @param timestamp
     * @return
     */
    private static String oppoSignature(String postData, String timestamp) {
        String result = DigestUtils.md5Hex(postData + timestamp + OPPOConfig.SALT).toLowerCase();
        return result;
    }


    /**
     * OPPO的AES加密
     *
     * @param ouId
     * @return
     */
    private static String oppoAES(String ouId) {
        final Key dataKey = new SecretKeySpec(Base64.decodeBase64(OPPOConfig.BASE_64_KEY),
            "AES");
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, dataKey);
            byte[] encryptData = cipher.doFinal(ouId.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBase64String(encryptData).replaceAll("\r",
                "").replaceAll("\n", "");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
/**
 * -----------------------------------------------------------小米----------------------------------------------------------
 */
    /**
     * 小米配置
     */
    private static class XiaoMiConfig {
        private static long APP_ID = 1215038;
        private static int CUSTOMER_ID = 887222;
        private static String HOST = "https://trail.e.mi.com";
        private static String PROD_URI = "/global/log";
        private static String TEST_URI = "/global/test";

        /**
         * 小米的上报根据事件类型不同，密钥也不同
         */
        @Getter
        @AllArgsConstructor
        public enum Keys {
            PAY("EiKKYSMSTVpUNRom", "xBnKSFKMaAlCISxZ"),
            REGISTER("caFkLCxmURiGnEiu", "LKAupPwzbHfxlBgV");

            private final String encrtptKey;
            private final String signKey;
        }
    }


    /**
     * 小米事件类型
     */
    @Getter
    @AllArgsConstructor
    public enum XiaoMiDataType {
        自定义激活("APP_ACTIVE"),
        自定义注册("APP_REGISTER"),
        自定义新增激活("APP_ACTIVE_NEW"),
        首次拉活("APP_RE_ACTIVE"),
        付费("APP_PAY"),
        首次付费("APP_FIRST_PAY"),
        留存("APP_RETENTION"),
        购买("APP_PURCHASE"),
        召回("APP_UNINSTALL"),
        拉活("APP_RE_ACTIVE_WAKE_UP"),
        IPVUV("APP_IPVUV"),
        关键行为("APP_ADDICTION"),
        授信("APP_CREDIT"),
        完件("APP_SUBMIT"),
        首单购买("APP_NEW_USER_PURCHASE"),
        加桌_仅快应用投放使用("APP_ADD_DESKTOP"),
        付费金额("APP_PAY_AMOUNT"),
        开户("APP_START_OPEN_ACCOUNT"),
        打开中间页("APP_OPEN_MIDDLE_PAGE"),
        打开店铺页("APP_OPEN_SHOP_PAGE"),
        店铺下单("APP_SHOP_ORDER"),
        线索("APP_INQUIRE"),
        自定义3日留存("APP_RETENTION_3D"),
        自定义4日留存("APP_RETENTION_4D"),
        自定义5日留存("APP_RETENTION_5D"),
        自定义6日留存("APP_RETENTION_6D"),
        自定义7日留存("APP_RETENTION_7D");

        private final String code;
    }


    /**
     * 小米数据上报实体类
     */
    @Data
    @Builder
    private static class UploadInfo {
        //info设备信息
        private String oaid;
        private long convTime;
        //conversion转化信息
        private long appId;
        private int customerId;
        private String convType;
        private String signKey;
        private String encryptKey;
        //生成的信息
        //最终url
        private String finalUrl;
        //最终生成的info
        private String finalInfo;
        //queryString 设备信息
        private String queryString;
        private String property;
        private String baseData;
        //md5后的sign值
        private String signature;

        @SneakyThrows
        public UploadInfo genInfo() {
            List<String> kvParam = new ArrayList<>();
            if (StringUtils.isNotBlank(this.oaid)) {
                kvParam.add("oaid=" + URLEncoder.encode(this.oaid, "UTF-8"));
            }
            if (this.convTime > 0) {
                kvParam.add("conv_time=" + URLEncoder.encode(String.valueOf(this.convTime), "UTF-8"));
            }

            final String ip = ServletUtils.getClientIP(ServletUtils.getRequest());
            kvParam.add("client_ip=" + URLEncoder.encode(ip, "UTF-8"));
            //1.按照顺序使用 & 拼接key=urlEncode(value),queryString
            this.queryString = String.join("&", kvParam);
            //2.按照顺序使用 & 拼接签名密钥signKey 和 urlEncode(queryString),得到property
            this.property = this.signKey + "&" + URLEncoder.encode(this.queryString, "UTF-8");
            log.info("===========>xiaomi_property:{}<===========", this.property);
            //3. 将字符串(property) 进行md5(property)加密,得到32位小写字符串signature
            this.signature = getMd5Digest(this.property);

            //4.按照顺序使用 & 拼接 queryString(注意是第一个步骤生成的) 和 sign=signature
            this.baseData = this.queryString + "&sign=" + this.signature;
            log.info("===========>xiaomi_base_data:{}<===========", this.baseData);
            //5.baseData进行加密 Base64(simple_xor{base_data, encrypt_key}),得到info信息
            byte[] resultChar = encrypt(this.baseData, this.encryptKey);
            this.finalInfo = com.sun.org.apache.xerces.internal.impl.dv.util.Base64.encode(resultChar);

            return this;
        }

        @SneakyThrows
        public String getTheFinalUrl(String uri) {
            return XiaoMiConfig.HOST + uri
                + "?appId=" + URLEncoder.encode(String.valueOf(this.appId), "UTF-8")
                + "&info=" + URLEncoder.encode(this.finalInfo, "UTF-8")
                + "&conv_type=" + URLEncoder.encode(this.convType, "UTF-8")
                + "&customer_id=" + URLEncoder.encode(String.valueOf(this.customerId), "UTF-8");
        }

        //对baseData进行加密 Base64(simple_xor{base_data, encrypt_key})
        public static byte[] encrypt(String info, String key) {
            try {
                if (StringUtils.isEmpty(info) || StringUtils.isEmpty(key)) {
                    return null;
                }
                //6.1 base_data, encrypt_key 异或加密
                char[] infoChar = info.toCharArray();
                char[] keyChar = key.toCharArray();

                byte[] resultChar = new byte[infoChar.length];
                for (int i = 0; i < infoChar.length; i++) {
                    resultChar[i] = (byte) ((infoChar[i] ^ keyChar[i % keyChar.length]) & 0xFF);
                }
                //6.2 Base64 编码
                return resultChar;
            } catch (Exception e) {
                return null;
            }
        }

        //md5 加密得到32位小写字符串.
        public static String getMd5Digest(String pInput) {
            try {
                MessageDigest lDigest = MessageDigest.getInstance("MD5");
                lDigest.update(pInput.getBytes("UTF-8"));
                BigInteger lHashInt = new BigInteger(1, lDigest.digest());
                return String.format("%1$032x", lHashInt);
            } catch (Exception e) {

            }
            return "";
        }
    }

    /**
     * 小米数据上报
     *
     * @param oaid
     * @param convType
     */
    public static void xiaoMiUpload(String oaid, DataType convType) {
        XiaoMiConfig.Keys keys;
        switch (convType) {
            case 支付:
                keys = XiaoMiConfig.Keys.PAY;
                break;
            case 注册:
                keys = XiaoMiConfig.Keys.REGISTER;
                break;
            default:
                return;
        }
        UploadInfo uploadInfo = UploadInfo.builder()
            .oaid(oaid)
            .convTime(System.currentTimeMillis())
            .convType(convType.getXiaomi())
            .appId(XiaoMiConfig.APP_ID)
            .customerId(XiaoMiConfig.CUSTOMER_ID)
            .signKey(keys.getSignKey())
            .encryptKey(keys.getEncrtptKey())
            .build()
            .genInfo();

        String url = uploadInfo.getTheFinalUrl(XiaoMiConfig.PROD_URI);
        log.info("===========>xiaomi_url:{}<===========", url);
        String result = HttpUtil.get(url);
        log.info("===========>xiaomi_result:{}<===========", result);
    }


    public static void main(String[] args) {
        xiaoMiUpload("cd6cc50952857732", DataType.注册);
    }
}
